package com.star.io.netty.socket;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;  
import io.netty.handler.codec.ByteToMessageDecoder;

import java.nio.charset.StandardCharsets;
import java.util.List;
  
public class CustomFrameDecoder extends ByteToMessageDecoder {
  
    private static final byte START_CODE = 0x68;  
    private static final byte END_CODE = 0x16;  
  
    @Override  
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 检查是否有足够的字节来读取起始码  
        if (in.readableBytes() < 1 || in.readByte() != START_CODE) {  
            // 如果没有起始码或者字节不足，则等待更多数据  
            return;  
        }  
  
        // 回退一个字节，因为我们已经读取了起始码，现在需要重新添加到缓冲区中以便后续处理  
        in.readerIndex(in.readerIndex() - 1);  
  
        // 计算我们需要的最小字节数（起始码 + 设备ID + 控制字 + 数据域长度（2字节） + 至少1字节的数据域 + 校验码 + 结束码）  
        int minFrameLength = 1 + 6 + 1 + 2 + 1 + 1 + 1; // 注意：这里的数据域长度至少为1，但实际情况可能更长  
  
        // 检查是否有足够的字节来读取完整的帧  
        if (in.readableBytes() < minFrameLength) {  
            // 如果没有足够的字节，则等待更多数据  
            return;  
        }  
  
        // 读取完整的帧  
        byte startCode = in.readByte(); // 起始码  
        byte[] deviceId = new byte[6];  
        in.readBytes(deviceId); // 设备ID  
        byte command = in.readByte(); // 控制字  
        short dataLength = in.readShort(); // 数据域长度  
  
        // 检查是否有足够的字节来读取数据域  
        if (in.readableBytes() < dataLength) {  
            // 如果没有足够的数据域字节，则将读取器索引重置为起始码之后的位置，并等待更多数据  
            in.readerIndex(1); // 重置到起始码之后的第一个字节  
            return;  
        }  
  
        byte[] data = new byte[dataLength];  
        in.readBytes(data); // 数据域  
        byte checkCode = in.readByte(); // 校验码（这里可能需要你实现校验逻辑）  
        byte endCode = in.readByte(); // 结束码  
  
        // 校验帧的正确性（这里可以根据你的业务逻辑添加校验码验证）  
        if (endCode != END_CODE) {  
            // 如果结束码不正确，则可能需要处理错误或者丢弃这个帧  
            throw new IllegalArgumentException("Invalid end code");  
        }  
  
        // 如果一切顺利，将解析后的帧添加到输出列表中（这里你可以将帧封装成一个更高级别的对象）  
         out.add(new ProtoPacket(startCode, deviceId, command, dataLength, new String(data, StandardCharsets.UTF_8), checkCode, endCode));
    }  
}